/**
 * P2P 文件上传和分片服务器
 * 提供文件上传、分片存储和下载功能
 */

const express = require('express');
const cors = require('cors');
const multer = require('multer');
const path = require('path');
const fs = require('fs').promises;
const crypto = require('crypto');

const app = express();
const PORT = process.env.PORT || 3000;

// 存储目录
const UPLOAD_DIR = path.join(__dirname, 'uploads');
const SHARDS_DIR = path.join(__dirname, 'shards');

// 确保目录存在
async function ensureDirectories() {
  await fs.mkdir(UPLOAD_DIR, { recursive: true });
  await fs.mkdir(SHARDS_DIR, { recursive: true });
}

// 配置文件上传
const storage = multer.diskStorage({
  destination: async (req, file, cb) => {
    await ensureDirectories();
    cb(null, UPLOAD_DIR);
  },
  filename: (req, file, cb) => {
    const uniqueName = `${Date.now()}-${file.originalname}`;
    cb(null, uniqueName);
  }
});

const upload = multer({ 
  storage,
  limits: { fileSize: 1024 * 1024 * 1024 } // 1GB 限制
});

// 文件数据库（内存存储，实际应用应使用数据库）
const fileDatabase = new Map();

app.use(cors());
app.use(express.json());

/**
 * 将文件分片
 */
async function shardFile(filePath, filename, shardSize = 100 * 1024) {
  const fileBuffer = await fs.readFile(filePath);
  const totalSize = fileBuffer.length;
  const shardCount = Math.ceil(totalSize / shardSize);
  
  const fileId = crypto.randomBytes(16).toString('hex');
  const shardDir = path.join(SHARDS_DIR, fileId);
  await fs.mkdir(shardDir, { recursive: true });
  
  const shards = [];
  for (let i = 0; i < shardCount; i++) {
    const start = i * shardSize;
    const end = Math.min(start + shardSize, totalSize);
    const shardData = fileBuffer.slice(start, end);
    
    const shardPath = path.join(shardDir, `shard_${i}.bin`);
    await fs.writeFile(shardPath, shardData);
    
    shards.push({
      index: i,
      size: shardData.length,
      path: shardPath
    });
  }
  
  return {
    fileId,
    filename,
    totalSize,
    shardSize,
    shardCount,
    shards
  };
}

/**
 * 生成 Reed-Solomon 校验分片配置
 * 注意：当前版本不生成实际的RS冗余分片，所有分片都是数据分片
 */
function generateRSConfig(shardCount) {
  // 当前简化实现：不生成冗余分片，所有分片都是数据分片
  // 如果需要真正的RS码，需要实现 RS 编码逻辑
  return {
    dataShards: shardCount,  // 所有分片都是数据分片
    parityShards: 0          // 没有冗余分片
  };
  
  // TODO: 未来版本可以实现真正的RS编码
  // const redundancy = 0.2;
  // const dataShards = Math.floor(shardCount / (1 + redundancy));
  // const parityShards = Math.floor(shardCount * redundancy);
  // 然后使用 RSCodec 生成实际的冗余分片
}

// 上传文件
app.post('/upload', upload.single('file'), async (req, res) => {
  try {
    if (!req.file) {
      return res.status(400).json({ error: '没有上传文件' });
    }
    
    const filePath = req.file.path;
    const originalName = req.file.originalname;
    
    console.log(`正在处理文件: ${originalName}`);
    
    // 分片文件
    const fileInfo = await shardFile(filePath, originalName);
    
    // 生成 RS 配置
    const rsConfig = generateRSConfig(fileInfo.shardCount);
    
    // 存储文件信息
    fileDatabase.set(fileInfo.fileId, {
      ...fileInfo,
      rsConfig,
      uploadedAt: new Date(),
      originalPath: filePath
    });
    
    // 生成 P2P 下载链接
    const serverUrl = `http://localhost:${PORT}`;
    const p2pLink = `p2p://${originalName}?peers=${serverUrl}&shards=${fileInfo.shardCount}&rs=${rsConfig.dataShards},${rsConfig.parityShards}&fileId=${fileInfo.fileId}`;
    
    console.log(`文件上传成功: ${originalName}`);
    console.log(`文件ID: ${fileInfo.fileId}`);
    console.log(`P2P链接: ${p2pLink}`);
    
    res.json({
      success: true,
      fileId: fileInfo.fileId,
      filename: originalName,
      size: fileInfo.totalSize,
      shardCount: fileInfo.shardCount,
      rsConfig,
      p2pLink,
      directLink: `${serverUrl}/download/${fileInfo.fileId}`
    });
  } catch (error) {
    console.error('上传错误:', error);
    res.status(500).json({ error: error.message });
  }
});

// 获取文件元数据
app.get('/metadata', (req, res) => {
  const { file, fileId } = req.query;
  
  let id = fileId;
  
  // 如果通过 file 参数传递 fileId
  if (file && file.includes('fileId=')) {
    const match = file.match(/fileId=([a-f0-9]+)/);
    if (match) id = match[1];
  }
  
  if (!id) {
    return res.status(400).json({ error: '缺少 fileId 参数' });
  }
  
  const fileInfo = fileDatabase.get(id);
  
  if (!fileInfo) {
    return res.status(404).json({ error: '文件不存在' });
  }
  
  // 返回元数据，所有分片都在当前节点
  const shards = Array(fileInfo.shardCount).fill(null).map(() => [0]);
  
  res.json({
    totalSize: fileInfo.totalSize,
    shardSize: fileInfo.shardSize,
    shards
  });
});

// 获取分片
app.get('/shard', async (req, res) => {
  const { file, fileId, index } = req.query;
  
  let id = fileId;
  
  // 从 file 参数提取 fileId
  if (file && file.includes('fileId=')) {
    const match = file.match(/fileId=([a-f0-9]+)/);
    if (match) id = match[1];
  }
  
  if (!id || index === undefined) {
    return res.status(400).json({ error: '缺少参数' });
  }
  
  const fileInfo = fileDatabase.get(id);
  
  if (!fileInfo) {
    return res.status(404).json({ error: '文件不存在' });
  }
  
  const shardIndex = parseInt(index);
  
  if (shardIndex < 0 || shardIndex >= fileInfo.shards.length) {
    return res.status(404).json({ error: '分片索引无效' });
  }
  
  try {
    const shardPath = fileInfo.shards[shardIndex].path;
    const shardData = await fs.readFile(shardPath);
    
    res.set('Content-Type', 'application/octet-stream');
    res.send(shardData);
  } catch (error) {
    console.error('读取分片错误:', error);
    res.status(500).json({ error: '读取分片失败' });
  }
});

// 直接下载完整文件
app.get('/download/:fileId', async (req, res) => {
  const { fileId } = req.params;
  const fileInfo = fileDatabase.get(fileId);
  
  if (!fileInfo) {
    return res.status(404).json({ error: '文件不存在' });
  }
  
  try {
    const fileData = await fs.readFile(fileInfo.originalPath);
    
    res.set('Content-Type', 'application/octet-stream');
    res.set('Content-Disposition', `attachment; filename="${encodeURIComponent(fileInfo.filename)}"`);
    res.send(fileData);
  } catch (error) {
    console.error('下载错误:', error);
    res.status(500).json({ error: '下载失败' });
  }
});

// 列出所有文件
app.get('/files', (req, res) => {
  const files = Array.from(fileDatabase.values()).map(file => ({
    fileId: file.fileId,
    filename: file.filename,
    size: file.totalSize,
    shardCount: file.shardCount,
    rsConfig: file.rsConfig,
    uploadedAt: file.uploadedAt,
    p2pLink: `p2p://${file.filename}?peers=http://localhost:${PORT}&shards=${file.shardCount}&rs=${file.rsConfig.dataShards},${file.rsConfig.parityShards}&fileId=${file.fileId}`,
    directLink: `http://localhost:${PORT}/download/${file.fileId}`
  }));
  
  res.json({ files });
});

// 删除文件
app.delete('/files/:fileId', async (req, res) => {
  const { fileId } = req.params;
  const fileInfo = fileDatabase.get(fileId);
  
  if (!fileInfo) {
    return res.status(404).json({ error: '文件不存在' });
  }
  
  try {
    // 删除分片目录
    const shardDir = path.join(SHARDS_DIR, fileId);
    await fs.rm(shardDir, { recursive: true, force: true });
    
    // 删除原文件
    await fs.unlink(fileInfo.originalPath).catch(() => {});
    
    // 从数据库删除
    fileDatabase.delete(fileId);
    
    res.json({ success: true });
  } catch (error) {
    console.error('删除错误:', error);
    res.status(500).json({ error: '删除失败' });
  }
});

// 健康检查
app.get('/health', (req, res) => {
  res.json({
    status: 'healthy',
    version: '1.0.0',
    uptime: process.uptime(),
    filesCount: fileDatabase.size
  });
});

// 启动服务器
async function start() {
  await ensureDirectories();
  
  app.listen(PORT, () => {
    console.log('\n=================================');
    console.log('🚀 P2P 文件服务器已启动');
    console.log(`📡 服务器地址: http://localhost:${PORT}`);
    console.log(`📁 上传目录: ${UPLOAD_DIR}`);
    console.log(`📦 分片目录: ${SHARDS_DIR}`);
    console.log('=================================\n');
    console.log('可用接口:');
    console.log(`  POST   /upload        - 上传文件`);
    console.log(`  GET    /files         - 列出所有文件`);
    console.log(`  GET    /metadata      - 获取文件元数据`);
    console.log(`  GET    /shard         - 获取文件分片`);
    console.log(`  GET    /download/:id  - 直接下载文件`);
    console.log(`  DELETE /files/:id     - 删除文件`);
    console.log('=================================\n');
  });
}

start().catch(console.error);

module.exports = app;


